package com.taotao.anno.sg;

import com.google.auto.service.AutoService;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * 自定义注解处理器
 */
@AutoService(Processor.class)
public class SetterProcessor extends AbstractProcessor {


    @Override
    public Set<String> getSupportedAnnotationTypes() {
        // 与注解 @SupportedAnnotationTypes({"com.taotao.anno.sg.Data"})  功能相同
        return new HashSet<>(Arrays.asList(SetterAnnotation.class.getName()));
    }


    @Override
    public SourceVersion getSupportedSourceVersion() {
        //设置为能够支持最新版本   @SupportedSourceVersion 等效
        return SourceVersion.latestSupported();
    }


    private Messager messager;
    private JavacTrees javacTrees;
    private TreeMaker treeMaker;
    private Names names;

    /**
     * JavacTrees 提供了待处理的抽象语法树
     * TreeMaker 封装了创建AST节点的一些方法
     * Names 提供了创建标识符的方法
     */
    @Override
    public synchronized void init(ProcessingEnvironment environment) {
        super.init(environment);
        this.messager = environment.getMessager();
        this.javacTrees = JavacTrees.instance(environment);
        Context context = ((JavacProcessingEnvironment) environment).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotation, RoundEnvironment roundEnv) {
//        processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>SetterAnnotation");
        Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(SetterAnnotation.class);
        elementsAnnotatedWith.forEach(e -> {
            //获取JCTree对象
            JCTree tree = javacTrees.getTree(e);
            tree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    //定义一个TO DO list
                    List<JCTree.JCVariableDecl> declList = List.nil();
                    System.out.println("类名:" + jcClassDecl.name);
                    //遍历抽象树中的所有属性
                    for (JCTree jcTree : jcClassDecl.defs) {
                        if (jcTree.getKind().equals(Tree.Kind.VARIABLE)) {
                            System.out.println("变量信息：" + jcTree.toString());
                            //过滤掉只处理类属性
                            JCTree.JCVariableDecl jcDecl = (JCTree.JCVariableDecl) jcTree;
                            declList = declList.append(jcDecl);
                        }
                    }
                    //对TO DO List遍历
                    declList.forEach(decl -> {
                        //messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + "has been processed");
                        JCTree.JCMethodDecl methodDecl = generateMethodDecl(decl);
                        jcClassDecl.defs = jcClassDecl.defs.prepend(methodDecl);
                    });
                    super.visitClassDef(jcClassDecl);
                }
            });
        });

        return true;
    }

    /**
     * 根据类属性描述生成 方法描述
     *
     * @param variableDecl 类属性标识
     */
    private JCTree.JCMethodDecl generateMethodDecl(JCTree.JCVariableDecl variableDecl) {
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        //1.方法表达式
        //左表达式 生成 this.name
        JCIdent thisN = treeMaker.Ident(names.fromString("this"));
        JCFieldAccess jcFieldAccess = treeMaker.Select(thisN, variableDecl.getName());
        //右表达式 name
        JCIdent name = treeMaker.Ident(variableDecl.getName());
        //左右表达式拼接后，生成表达式 this.name = name;
        JCTree.JCExpressionStatement statement = createExecExp(jcFieldAccess, name);
        statements.append(statement);
        //创建组合语句
        JCTree.JCBlock block = treeMaker.Block(0, statements.toList());

        //2.方法参数
        //创建访问标志语法节点
        JCModifiers jcModifiers = treeMaker.Modifiers(Flags.PARAMETER);
        JCTree.JCVariableDecl param = treeMaker.VarDef(jcModifiers, variableDecl.getName(), variableDecl.vartype, null);
        List<JCTree.JCVariableDecl> parameters = List.of(param);

        //3.方法返回表达式
        JCTree.JCExpression methodType = treeMaker.Type(new Type.JCVoidType());
        JCModifiers publicModifiers = treeMaker.Modifiers(Flags.PUBLIC);
        Name newName = transformName(variableDecl.getName());
        return treeMaker.MethodDef(publicModifiers, newName, methodType, List.nil(), parameters, List.nil(), block, null);
    }

    private Name transformName(Name name) {
        String s = name.toString();
        return names.fromString("set" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
    }

    /**
     * 创建可执行语句语法树
     *
     * @param lhs 做表达时候
     * @param rhs 右表达式
     */
    private JCTree.JCExpressionStatement createExecExp(JCTree.JCExpression lhs, JCTree.JCExpression rhs) {
        return treeMaker.Exec(this.createAssign(lhs, rhs));
    }

    /**
     * 创建赋值语句语法树
     *
     * @param lhs 左表达式
     * @param rhs 右表达式
     */
    private JCAssign createAssign(JCTree.JCExpression lhs, JCTree.JCExpression rhs) {
        return treeMaker.Assign(lhs, rhs);
    }
}